﻿/* Autoduel Game
 * CS4911 Senior Design Project
 * Fall 2010 Semester
 * Ian Axelrod, Liz Ha, Andrew Muldowney, Edward Futch
 */
using System;
using System.Collections.Generic;

using Microsoft.Xna.Framework;
using AutoduelClient.GameClient.Player;

namespace AutoduelClient.GameClient.World
{
    /// <summary>
    /// Represents client's view of the autoduel world
    /// </summary>
    public class World
    {
	    /// <summary>
        /// The number of grid columns (server world)
	    /// </summary>
	    public static int GRIDS_X = 5;

        /// <summary>
        /// The number of grid rows (server world)
        /// </summary>
	    public static int GRIDS_Y = 5;

        #region Fields (6)
		// the set of visible grids
        private Grid[,] grids = { { new Grid(), new Grid(), new Grid() },
                                 { new Grid(), new Grid(), new Grid() },
                                 { new Grid(), new Grid(), new Grid() } };

		// our player
        private Player.Player localPlayer;

		// a reference to the game
        private AutoduelClient.GameUI.Game game;

		// marker to indicate when the serverPlayers dictionary
        // is ready to be used
        private bool serverPlayersReady;

		// all the players visible to us
        private Dictionary<string, Player.Player> players = new Dictionary<string, Player.Player>();
		// all the players in the server database
        // only populated when admin and when requested
        // not necessarily up-to-date
        private Dictionary<string, Player.Player> serverPlayers = new Dictionary<string, Player.Player>();
		#endregion

		#region Properties (6)
		/// <summary>
        /// Get/Set the reference to the game
        /// </summary>
        public AutoduelClient.GameUI.Game Game
        {
            get { return game; }
            set { game = value; }
        }
		/// <summary>
        /// Gets/Sets the set of visible grids
        /// </summary>
        public Grid[,] Grids
        {
            get { return grids; }
            set { grids = value; }
        }
		/// <summary>
        /// Gets/Sets the local player for this world (us)
        /// </summary>
        public Player.Player LocalPlayer
        {
            get { return localPlayer; }
            set { localPlayer = value; }
        }
		/// <summary>
        /// Gets/Sets the players visible to us
        /// </summary>
        public Dictionary<string, Player.Player> Players
        {
            get { return players; }
            set { players = value; }
        }
		/// <summary>
        /// Gets/Sets the players retrieved from the server database
        /// </summary>
        public Dictionary<string, Player.Player> ServerPlayers
        {
            get { return serverPlayers; }
            set { serverPlayers = value; }
        }
		/// <summary>
        /// Get/Set the marker used to indicate when the serverPlayers dictionary
        /// is ready to be used
        /// </summary>
        public bool ServerPlayersReady
        {
            get { return serverPlayersReady; }
            set { serverPlayersReady = value; }
        }
		#endregion

		#region Methods (8)

		/// <summary>
        /// Adds a player to the world
        /// </summary>
        /// <param name="player">The player to add</param>
        public void AddPlayer(Player.Player player)
        {
            System.Diagnostics.Debug.WriteLine("Player added to world.");
            this.players.Add(player.Name, player);
        }

		/// <summary>
        /// Requests a list of all players stored in the server database
        /// from the server
        /// </summary>
		public void BeginGetAllPlayersFromServer()
        {
            this.serverPlayersReady = false;
            this.ServerPlayers.Clear();
            this.game.Client.SendMessage(new byte[] { (byte)OPCODES.REQ_UPDPLAYERLIST });
        }

        public Grid DetermineLocalPostionByGrid(Coordinate point)
        {
            return this.grids[point.Y, point.X];
            //We swap the coords here due to the fact that we go from a 1D array on the server to a 2D array here on the client
            //The 2D array indexes first the Row, then Column. Which for all intents and purposes here is (y,x).

        }
		/// <summary>
        /// Takes in coordinates and determines what grid to retrieve from the local world
        /// </summary>
        /// <param name="point">The server coordinates used to retrieve the grid </param>
        /// <returns>The local world grid</returns>
        public Grid DetermineGridFromCoords(Coordinate point)
        {
            // grids[1,1] is always the grid the current player is in,
            // and is also the only grid that is always guaranteed to have
            // the most up-to-date coordinates, so it is our pivot
            Coordinate pivot = this.grids[1, 1].RealCoords;

            if (pivot.X > point.X && pivot.Y > point.Y)
            {
                return this.grids[0, 0];
            }
            else if (pivot.X == point.X && pivot.Y > point.Y)
            {
                return this.grids[0, 1];
            }
            else if (pivot.X > point.X && pivot.Y == point.Y)
            {
                return this.grids[1, 0];
            }
            else if (pivot.X == point.X && pivot.Y == point.Y)
            {
                return this.grids[1, 1];
            }
            else if (pivot.X < point.X && pivot.Y > point.Y)
            {
                return this.grids[0, 2];
            }
            else if (pivot.X < point.X && pivot.Y == point.Y)
            {
                return this.grids[1, 2];
            }
            else if (pivot.X > point.X && pivot.Y < point.Y)
            {
                return this.grids[2, 0];
            }
            else if (pivot.X == point.X && pivot.Y < point.Y)
            {
                return this.grids[2, 1];
            }
            else
            {
                return this.grids[2, 2];
            }
        }

		/// <summary>
        /// Takes in coordinates and determines what grid to retrieve based on the relative position of th eplayer to the top-left grid
        /// </summary>
        /// <param name="point">The server coordinates used to retrieve the grid</param>
        /// <returns>The local world grid</returns>
        /// 
        public Grid DetermineGridFromCoordsRelative(Coordinate point)
        {
            // the relative position of the player as compared to the grid set on the client mod the size of a grid, equals the grid its in.
            Coordinate relativePosition = new Coordinate(((Math.Abs(point.X - this.grids[0, 0].RealCoords.X) / this.game.ScreenWidth) % 3), (Math.Abs(point.Y - this.grids[0, 0].RealCoords.Y) / this.game.ScreenHeight) % 3);

            System.Diagnostics.Debug.WriteLine("Relative Pos: " + relativePosition.ToString());

            return this.grids[relativePosition.X,relativePosition.Y];

        }

		/// <summary>
        /// Returns a player in the world by its name
        /// </summary>
        /// <param name="realPlayerName">The name of the player</param>
        /// <returns>The player, if it exists, otherwise null</returns>
        public Player.Player GetPlayerByName(string realPlayerName)
        {
            Player.Player result;

            if (this.players.TryGetValue(realPlayerName, out result))
            {
                return result;
            }
            else
            {
                return null;
            }
        }

		/// <summary>
        /// Searches the list of players retrieved from the server
        /// and returns the player that has that name
        /// </summary>
        /// <param name="realPlayerName">The real player name (Duelist: concatenated)</param>
        /// <returns>The player thta has the name</returns>
        public Player.Player GetPlayerByNameServer(string realPlayerName)
        {
            Player.Player result;

            if (this.serverPlayers.TryGetValue(realPlayerName, out result))
            {
                return result;
            }
            else
            {
                return null;
            }
        }

		/// <summary>
        /// Removes a player from the world
        /// </summary>
        /// <param name="player">The player to remove</param>
        public void RemovePlayer(Player.Player player)
        {
            // need to remove player from the grid first, if he is on one
            if (player.CurrentGrid != null)
            {
                player.CurrentGrid.RemovePlayer(player);
            }

            System.Diagnostics.Debug.WriteLine("Player removed from world.");
            this.players.Remove(player.Name);
        }

		/// <summary>
        /// Takes in a grid and refreshes data inside of it
        /// </summary>
        /// <param name="tmpGrid">The data to use to update the grid</param>
        public void UpdateGrid(Grid tmpGrid)
        {
            System.Diagnostics.Debug.WriteLine("Updating grid: " + tmpGrid);
            if (tmpGrid.IsPlayerGrid == 0)
            {
                Grid targetGrid = DetermineLocalPostionByGrid(tmpGrid.Index);
                //Grid targetGrid = DetermineGridFromCoords(tmpGrid.RealCoords);

                targetGrid.Locations.Clear();
                targetGrid.RealCoords = tmpGrid.RealCoords;
                targetGrid.Background = tmpGrid.Background;
                targetGrid.BgResource = tmpGrid.BgResource;
            }
            else
            {
                this.grids[1, 1].Locations.Clear();
                this.grids[1, 1].RealCoords = tmpGrid.RealCoords;
                this.grids[1, 1].Background = tmpGrid.Background;
                this.grids[1, 1].BgResource = tmpGrid.BgResource;
            }
        }

		#endregion
    }
}
